    <h1 class="pageHeader">Spica Framework - Manual - Hand-on guide</h1>

    <h2 class="header">Hướng dẫn chi tiết (How-to) - Kiến trúc event-oriented và các event trong Spica</h2>
    <p>Để chu kỳ sống của ứng dụng cho Spica quản lý có tính cơ động và dễ tùy biến, Spica hỗ trợ một mô hình có tên là 
    kiến trúc hướng sự kiện <code>event-oriented</code>. Kiến trúc này tạo ra các điểm chặn hay <code>event</code> trong luồng thực thi của ứng dụng tại vị trí quan trọng
    (critical point) của framework hay ứng dụng mà người lập trình có thể tận dụng để
    can thiệp vào quá trình xử lý một cách tuần tự.</p>
    <h3>Khái niệm</h3>
    <p>Trong Spica, sự kiện là một điểm thực thi của hệ thống (mức framework hay mức
    application) mà ở đó một thông báo cụ thể sẽ được gửi đến cho các thành phần
    quản lý sự kiện để các thành phần này có các phản ứng thích hợp. </p>
    <h3>Các sự kiện ở mức hệ thống trong Spica</h3>
    <p>Sự kiện hệ thống là sự kiện xảy ra trong quá trình xử lý request tại bộ khởi tạo context
    và dispatcher. Trong quá trình này lập trình viên có thể tận dụng các sự kiện phát sinh
    để can thiệp vào các quá trình:</p>
    <ul>
      <li><code>spica_context_start</code>: Sự kiện xuất hiện sau khi
          <ul>
              <li>nạp file <code>init.php</code> hay là xác định tên application</li>
              <li>Xây dựng include_path cho hệ thống</li>
              <li>Nạp file <code>global.php</code></li>              
              <li>Xác định <code>bootstrap_path</code>, <code>app_path</code> và tên của application trong biến global của hệ thống</li>
              <li>nạp file <code>context.php</code></li>
              <li>Kích hoạt cơ chế xử lý lỗi mặc định</li>
          </ul>
      Developer có thể dùng event này để kiểm tra quá trình xử lý context mà user cài đặt tại <code>context.php</code> hay
      thay đổi cơ chế theo dõi error xuất hiện (ErrorListener)</li>
      <li><code>spica_url_resolver_start</code>: Sự kiện xuất hiện sau khi quá trình xác định package
      sẽ phục vụ request diễn ra và trước khi xác định đối tượng sẽ biên dịch URL thành các
      tên gọi thành phần phục vụ request mà Spica hiểu được.</li>
      <li><code>spica_url_resolver_stop</code>: Sự kiện xuất hiện ngay sau khi bộ biện dịch URL
      xử lý tên của route dùng để map URL vào tên gọi các thành phần phục vụ request. Khi
      sự kiện này xuất hiện thì <code>SpicaContext::getRoute()</code> sẽ có giá trị khác null.
      Nếu <code>SpicaContext::getRoute()</code> trả về false thì có nghĩa là việc biên dịch URL
      không thành công và không xác định được thành phần phục vụ request. Tuy nhiên, sau khi xử lý này diễn ra, tên route 
      vẫn có thể là một giá trị <code>false</code>. Khi đó một exception có thể bị ném ra trước khi sự kiện <code>spica_dispatcher_start</code>
      dưới đây xuất hiện.</li>

      <li><code>spica_dispatcher_start</code>: Sự kiện xuất hiện sau sự kiện <code>spica_url_resolver_stop</code> và tên của route
      được xác định rõ ràng (xem ở trên). Sự kiện này diễn ra trước khi dispatcher thực hiện việc xử lý URL nội bộ (module, controller, action)
      nhằm tìm ra lớp Controller chịu trách nhiệm và thực hiện việc gọi phương thức phù hợp trên Controller này.</li>

      <li><code>spica_dispatcher_stop</code>: Sự kiện này diễn ra sau khi dispatcher thực hiện việc xử lý URL nội bộ (module, controller, action)
      nhằm tìm ra lớp Controller chịu trách nhiệm và thực hiện việc gọi phương thức phù hợp trên Controller này. Khi đó response đã chuẩn bị sẵn sàng
      hoặc các khâu xử lý chính thức đã được thực thi. Như vậy sự kiện này có thể được dùng để log hay ghi chép các thông tin cần thiết mà qua trình xử lý tạo ra
      hoặc thực hiện cache nội dung cần thiết.</li>
    </ul>

    <h3>Khai thác các sự kiện hệ thống</h3>

    <p>Để có thể tận dụng các sự kiện nhằm thay đổi hay thực thi các thủ tục tại các điểm cụ thể trong chu kỳ sống của ứng dụng, Spica
    cung một số phương thức để đăng ký các lớp xử lý sự kiện vào từng sự kiện cụ thể. Việc đăng ký này có thể thực hiện trên
    file <code>config/context.php</code></p>
    <p>Với PHP 5.3 có thể dùng một hàm nặc danh như là tham số thứ hai của phương thức <code>Spica::subscribeEvent</code></p>
<pre class="brush: php">
// Anonymous function in PHP 5.3
$contextStartCallback = function() {
    error_log("Context start. \n", 3, './log/events.log');
};

Spica::subscribeEvent('spica_context_start', $contextStartCallback);

Spica::subscribeEvent('spica_dispatcher_stop', function() {
    error_log("Dispatching stops. Response is ready.\n", 3, './log/events.log');
    error_log("Response content is:\n".SpicaResponse::getBody(), 3, './log/events.log');
    error_log("Response content is prepared by the Controller named ".
             get_class(SpicaContext::getDispatcher()->getLastController()), 3, './log/events.log');
});
</pre>

        <p>Với PHP 5.2 có thể dùng một lớp thực hiện giao diện <code>SpicaInvokable</code> như là tham số thứ hai của
        phương thức <code>Spica::subscribeEvent</code>. Giao diện <code>SpicaInvokable</code> đã được nạp sẵn trong hệ thống.</p>
<pre class="brush: php">
class ContextStartEventHandler implements SpicaInvokable
{
    public function __invoke()
    {
        error_log("Context start. \n", 3, './log/events.log');
    }
}

Spica::subscribeEvent('spica_context_start', new ContextStartEventHandler());
</pre>
    <p>Xem cách khai thác thủ tục hệ thống để xử lý <a href="docs/guide/page/pname/howto_pagecaching">Page caching</a>.</p>

    <h3>Khai thác các sự kiện cấp độ thành phần hệ thống</h3>
    <h4>Sự kiện <code>spica_layout_fetch_body</code></h4>
    <p>Sự kiện <code>spica_layout_fetch_body</code> là sự kiện phát sinh sau khi layout template gọi <code>fetchBody()</code>
    để sinh ra vùng nội dung trung tâm của trang nhưng trước khi có bất cứ logic xử lý nào để
    xác định vùng nội dung đó do đối tượng nào cung cấp. Chúng ta có thể lợi dụng sự kiện này để:</p>
    <ul>
      <li>Thực hiện 1 số thay tác log hay tiền xử lý</li>
      <li>Đọc dữ liệu cache và populate vào thuộc tính <code>precompiledContent['body_content']</code> để
      Spica có thể xuất nội dung mà không cần tạo ra một đối tượng BlockGroup nào. Việc đăng ký
      callback hay Closure cho sự kiện này cần phải được thực hiện trước khi <code>SpicaPageLayout::fetch()</code>
      diễn ra.</li>
    </ul>
    <p>Callback/SpicaInvokable hay Closure để đón nhận sự kiện này sẽ có một tham số chính là đối tượng page layout
    hiện thời.</p>
    <p>Ví dụ:</p>

<pre class="brush: php">
&lt;?php

class Welcome_DocsIndex
{
    public function indexAction()
    {
        $callback = function() {
            $layout = func_get_arg(0);
            $layout-&gt;precompiledContent['body_content'] = '';
            Spica::log('Logging ...');
        };
        Spica::subscribeEvent('spica_layout_fetch_body', $callback);

        $theme = SpicaContext::getTheme();
        $theme-&gt;pageTitle = 'Spica Framework - Manual';
        $theme-&gt;attachCss('index');
        $theme-&gt;attachMeta('index');
        $theme-&gt;setBody('welcome/SpicaDocsIndexBlockGroup');
        SpicaResponse::setBody($theme-&gt;fetch('vi_VN/docs_index'));
    }
}

?&gt;

</pre>
    <h4>Sự kiện <code>spica_blockgroup_fetch</code></h4>
    <p>Đây là sự kiện phát sinh khi có một blockGroup nào đó được gọi qua tên
    của nó dùng <code>SpicaPageLayout::fetchBlockGroup()</code>. Khác với <code>spica_layout_fetch_body</code>, callback
    của sự kiện này nhận hai tham số: tên gọi của block group và đối tượng page layout phát động sự kiện.</p>

    <h4>Sự kiện <code>spica_block_fetch</code></h4>
    <p>Đây là sự kiện phát sinh khi có một BlockGroup nào đó được gọi qua tên
    của nó dùng <code>SpicaPageBlockGroup::fetchBlock()</code>. Khác với <code>spica_blockgroup_fetch</code>, callback
    của sự kiện này nhận hai tham số: tên gọi của block và đối tượng block group phát động sự kiện.</p>

    <?php echo $this->fetchBlock('welcome/SpicaDocsNoteBlock'); ?>